module MemBus(
  input clk,
  input rst,

  output dcache_mem_rbusy,
  input dcache_mem_ren,
  input [3:0] dcache_mem_rsize,
  input [31:0] dcache_mem_raddr,
  output reg [63:0] dcache_mem_rdata,
  output reg dcache_mem_rdata_valid,

  output dcache_mem_wbusy,
  input dcache_mem_wen,
  input [3:0] dcache_mem_wsize,
  input [31:0] dcache_mem_waddr,
  input [63:0] dcache_mem_wdata,

  output icache_mem_rbusy,
  input icache_mem_ren,
  input [3:0] icache_mem_rsize,
  input [31:0] icache_mem_raddr,
  output reg [63:0] icache_mem_rdata,
  output reg icache_mem_rdata_valid,


  output clint_wen,
  output clint_ren,
  output [31:0] clint_raddr,
  output [31:0] clint_waddr,
  output [63:0] clint_wdata,
  output [3:0] clint_wsize,
  input [63:0] clint_rdata,
  output [3:0] clint_rsize,
  input clint_rdata_valid,

  input io_master_awready,
  output io_master_awvalid,
  output [31:0] io_master_awaddr,
  output [3:0] io_master_awid,
  output [7:0] io_master_awlen,
  output [2:0] io_master_awsize,
  output [1:0] io_master_awburst,

  input io_master_wready,
  output io_master_wvalid,
  output [63:0] io_master_wdata,
  output [7:0] io_master_wstrb,
  output io_master_wlast,

  output io_master_bready,
  input io_master_bvalid,
  input [1:0] io_master_bresp,
  input [3:0] io_master_bid,

  input io_master_arready,
  output io_master_arvalid,
  output [31:0] io_master_araddr,
  output [3:0] io_master_arid,
  output [7:0] io_master_arlen,
  output [2:0] io_master_arsize,
  output [1:0] io_master_arburst,

  output io_master_rready,
  input io_master_rvalid,
  input [1:0] io_master_rresp,
  input [63:0] io_master_rdata,
  input io_master_rlast,
  input [3:0] io_master_rid
);
  localparam FSM_SEL_ICACHE = 1'b0;
  localparam FSM_SEL_DCACHE = 1'b1;
  reg [0:0] rstate;
  
  wire sel_clint_w = dcache_mem_waddr[31:16] == 16'h0200;
  wire sel_clint_r = dcache_mem_raddr[31:16] == 16'h0200;
  
  reg icache_reading,dcache_reading;
  always@(posedge clk) begin
    if(rst) begin
      icache_reading <= 1'b0;
      dcache_reading <= 1'b0;
    end else begin
      if(rstate == FSM_SEL_ICACHE) begin
        if(icache_mem_ren) icache_reading <= 1'b1;
        if(icache_mem_rdata_valid) icache_reading <= 1'b0;
      end else begin
        icache_reading <= 1'b0;
      end
      if(rstate == FSM_SEL_DCACHE) begin
        if(dcache_mem_ren) dcache_reading <= 1'b1;
        if(dcache_mem_rdata_valid) dcache_reading <= 1'b0;
      end else begin
        dcache_reading <= 1'b0;
      end
    end
  end

  always@(posedge clk)begin
    if(rst) rstate <= FSM_SEL_ICACHE;
    else 
    case(rstate)
    FSM_SEL_ICACHE: if(dcache_mem_ren&(~icache_mem_ren)&(~icache_reading)) rstate <= FSM_SEL_DCACHE;
    FSM_SEL_DCACHE: if(icache_mem_ren&(~dcache_mem_ren)&(~dcache_reading)) rstate <= FSM_SEL_ICACHE;
    default:;
    endcase
  end

  always @(*) begin
    icache_mem_rdata = 64'b0;
    icache_mem_rdata_valid = 1'b0;
    dcache_mem_rdata = 64'b0;
    dcache_mem_rdata_valid = 1'b0;
    case(rstate)
    FSM_SEL_ICACHE: begin
      icache_mem_rdata         = io_master_rdata;
      icache_mem_rdata_valid   = io_master_rvalid;
    end
    FSM_SEL_DCACHE: begin
      dcache_mem_rdata         = {64{io_master_rvalid}}&io_master_rdata | {64{clint_rdata_valid}}&clint_rdata;
      dcache_mem_rdata_valid   = io_master_rvalid|clint_rdata_valid;
    end
    default:;
    endcase
  end

  assign icache_mem_rbusy = rstate != FSM_SEL_ICACHE;
  assign dcache_mem_rbusy = rstate != FSM_SEL_DCACHE;
  // assign dcache_mem_wbusy = awvalid|wvalid;

  localparam BYTES_TRANS_1 = 3'b000;
  localparam BYTES_TRANS_2 = 3'b001;
  localparam BYTES_TRANS_4 = 3'b010;
  localparam BYTES_TRANS_8 = 3'b011;

  reg [31:0] awaddr;
  reg [2:0] awsize;
  reg awvalid;
  reg wvalid;
  reg [63:0] wdata;

  reg wstate;

  assign dcache_mem_wbusy = wstate;
  always @(posedge clk) begin
    if(rst) wstate <= 1'b0;
    else
      case(wstate)
      1'b0: if(dcache_mem_wen&(~sel_clint_w)) wstate <= 1'b1;
      1'b1: if(wvalid&io_master_wready) wstate <= 1'b0;
      default: ;
      endcase
      
      
  end
  always @(posedge clk) begin
    if(rst) begin
      awaddr <= 32'b0;
      awsize <= 3'b0;
      awvalid <= 1'b0;
      wvalid <= 1'b0;
      wdata <= 64'b0;
    end else begin
      case(wstate)
      1'b0: begin
        wvalid <= 1'b0;
        if(dcache_mem_wen&(~sel_clint_w)) begin
          awvalid <= 1'b1;
          awaddr <= dcache_mem_waddr;
          wdata <= dcache_mem_wdata;
          case(dcache_mem_wsize)
          4'b0001:awsize <= BYTES_TRANS_1;
          4'b0010:awsize <= BYTES_TRANS_2;
          4'b0100:awsize <= BYTES_TRANS_4;
          4'b1000:awsize <= BYTES_TRANS_8;
          default: awsize <= 3'b000;
          endcase 
        end
      end
      1'b1: begin
        if(wvalid&io_master_wready) begin
          wvalid <= 1'b0;
        end
        if(awvalid&io_master_awready) begin
          wvalid <= 1'b1;
          awvalid <= 1'b0;
        end
      end
      default: begin
        awvalid <= 1'b0;
        wvalid <= 1'b0;
      end
      endcase
      
      
    end
  end
  reg arvalid;
  reg [31:0] araddr;
  reg [2:0] arsize;
  always @(posedge clk) begin
    if(rst) begin
      arvalid <= 1'b0;
      araddr <= 32'b0;
      arsize <= 3'b0;
    end else begin
      case(rstate)
      FSM_SEL_ICACHE: begin
        if(arvalid&io_master_arready) arvalid <= 1'b0;
        if(icache_mem_ren) begin
          arvalid <= 1'b1;
          araddr <= icache_mem_raddr;
          case(icache_mem_rsize)
          4'b0001:arsize <= BYTES_TRANS_1;
          4'b0010:arsize <= BYTES_TRANS_2;
          4'b0100:arsize <= BYTES_TRANS_4;
          4'b1000:arsize <= BYTES_TRANS_8;
          default: arsize <= 3'b000;
          endcase
        end
      end
      FSM_SEL_DCACHE: begin
        if(arvalid&io_master_arready) arvalid <= 1'b0;
        if(dcache_mem_ren&(~sel_clint_r)) begin
          arvalid <= 1'b1;
          araddr <= dcache_mem_raddr;
          case(dcache_mem_rsize)
          4'b0001:arsize <= BYTES_TRANS_1;
          4'b0010:arsize <= BYTES_TRANS_2;
          4'b0100:arsize <= BYTES_TRANS_4;
          4'b1000:arsize <= BYTES_TRANS_8;
          default: arsize <= 3'b000;
          endcase
        end
      end
      default:;
      endcase
    end
  end
  

  assign clint_ren = (~dcache_mem_rbusy)&dcache_mem_ren&sel_clint_r;
  assign clint_wen = (~dcache_mem_wbusy)&dcache_mem_wen&sel_clint_w;
  assign clint_raddr = dcache_mem_raddr;
  assign clint_waddr = dcache_mem_waddr;
  assign clint_wsize = dcache_mem_wsize;
  assign clint_rsize = dcache_mem_rsize;
  assign clint_wdata = dcache_mem_wdata;

  assign io_master_awvalid = awvalid;
  assign io_master_awaddr = awaddr;
  assign io_master_awid = 4'b0;
  assign io_master_awlen = 8'b0;
  assign io_master_awsize = awsize;
  assign io_master_awburst = 2'b0;

  assign io_master_wvalid = wvalid;
  assign io_master_wdata = wdata;
  assign io_master_wstrb = 8'hff;
  assign io_master_wlast = 1'b1;

  assign io_master_bready= 1'b1;

  assign io_master_arvalid = arvalid;
  assign io_master_araddr = araddr;
  assign io_master_arid = 4'b0;
  assign io_master_arlen = 8'b0;
  assign io_master_arsize = arsize;
  assign io_master_arburst = 2'b0;
  assign io_master_rready = 1'b1;
endmodule
